#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <pthread.h>
#include <fstream>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unordered_map>
#include "Socket.hpp"
#include "log.hpp"
const uint16_t defaultport = 8080;
const std::string wwwroot = "./wwwroot"; // web根目录
const std::string sep = "\r\n";
const std::string homepage = "index.html";

class HttpServer;

class ThreadData
{
public:
    ThreadData(int fd,HttpServer *s) : sockfd(fd),svr(s)
    {
    }

public:
    int sockfd;
    HttpServer *svr;
};
class HttpRequest
{
public:
    void Deserialize(std::string req)
    {
        while(true)
        {
            std::size_t pos = req.find(sep);
            if(pos ==std::string::npos) break;
            std::string temp = req.substr(0,pos);
            if(temp.empty()) break;
            req_header.push_back(temp);
            req.erase(0,pos+sep.size());
        }
        text = req;
    }
    void Parse()
    {
        std::stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;
        file_path = wwwroot; // ./wwwroot
        if(url == "/" || url == "/index.html")  
        {
            file_path += "/";
            file_path += homepage; //./wwwroot/index.html
        }
        else file_path += url; // /a/b/c/d.html -> ./wwwroot/a/b/c/d.html
        auto pos = file_path.rfind(".");
        if(pos == std::string::npos)  suffix = ".html";
        else suffix = file_path.substr(pos);
    }
    void DebugPrint()
    {
        std::cout << "---------------------------"<<std::endl;
        for(auto &line:req_header)
        {
            std::cout << line << std::endl << std::endl;
        }
        std::cout<< "method: " << method << std::endl;
        std::cout<< "url: " << url << std::endl;
        std::cout<< "http_version: " << http_version << std::endl;
        std::cout<< "file_path: " << file_path << std::endl;
        std::cout << text << std::endl;
        std::cout << "---------------------------"<<std::endl;
    }
public:
    std::vector<std::string> req_header;
    std::string text;
    // 解析之后的结果
    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path; 
    std::string suffix; //文件后缀
};
class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport) : port_(port)
    {
        content_type.insert({".html","text/html"});
        content_type.insert({".png","image/png"});
        content_type.insert({".jpg","image/jpeg"});
    }
    ~HttpServer()
    {
    }

public:
    bool Start()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        while (true)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = listensock_.Accept(&clientip, &clientport);
            if(sockfd < 0) continue;
            ThreadData *td = new ThreadData(sockfd,this);
            td->sockfd = sockfd;
            pthread_t tid;
            pthread_create(&tid, nullptr, _ThreadRun, td);
        }
    }

public:
    static std::string _ReadHtmlContent(const std::string &htmlpath)
    {
        std::ifstream in(htmlpath);
        if(!in.is_open()) return "";

        in.seekg(0,std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0,std::ios_base::beg);
        std::string content;
        content.resize(len);
        in.read((char*)content.c_str(),content.size());
        // std::string line,content;
        // while(std::getline(in,line))
        // {
        //     content += line;
        // }
        // in.close();
        return content;
    }
    std::string SuffixToDesc(const std::string &suffix)
    {
        auto iter = content_type.find(suffix);
        if(iter == content_type.end()) return content_type[".html"];
        else return content_type[suffix];
    }
    void _HttpHander(int sockfd)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer;
            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse();
            //req.DebugPrint();
            // 返回响应的过程
            std::string text;
            text = _ReadHtmlContent(req.file_path);
            bool flag = true;
            if(text.empty()) 
            {
                flag = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "error.html";
                text = _ReadHtmlContent(err_html);
            }
            std::string respon_line;
            if(flag)
                respon_line = "HTTP/1.0 200 OK\r\n";
            else
                respon_line = "HTTP/1.0 404 Not Found\r\n";
            //respon_line = "HTTP/1.0 302 Found\r\n";
            std::string respon_header = "Content-Length: ";
            respon_header += std::to_string(text.size());
            respon_header += "\r\n";
            respon_header += "Content-Type: ";
            respon_header += SuffixToDesc(req.suffix);
            respon_header += "\r\n";
            respon_header += "Set-Cookie: name=lizuobing";
            respon_header += "\r\n";
            respon_header += "Set-Cookie: passwd=12345";
            respon_header += "\r\n";
            respon_header += "Set-Cookie: view=index.html";
            respon_header += "\r\n";

            //respon_header += "Location: https://www.qq.com\r\n";
            std::string space_line = "\r\n";
            // 响应的三部分-状态行+报头+正文
            std::string response = respon_line;
            response += respon_header;
            response += space_line;
            response += text;
            // 将响应发送回
            send(sockfd,response.c_str(),response.size(),0);
        }
        close(sockfd);
    }
    static void *_ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->svr->_HttpHander(td->sockfd);
        delete td;
        return nullptr;
    }

private:
    MySocket listensock_;
    uint16_t port_;
    std::unordered_map<std::string,std::string> content_type;
};